// 轻量 MQTT 发布工具：按 v3 文档构造并上报示例数据，或发布原始Payload
// 用法：
//   mqtt-pub --config ./config data
//   mqtt-pub --config ./config raw --topic <topic> --payload '<json>'

#include "protocol/mqtt/MqttClient.h"
#include "utils/Config.h"
#include "core/Logger.h"

#include <openssl/hmac.h>
#include <openssl/evp.h>

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <thread>
#include <map>

using ems::protocol::MqttClient;
using ems::protocol::MqttQoS;
using ems::utils::Config;
using ems::utils::MqttConfig;

static std::string hexEncode(const unsigned char* data, size_t len) {
    static const char* hex = "0123456789abcdef";
    std::string out; out.resize(len * 2);
    for (size_t i=0;i<len;++i){ out[2*i]=hex[(data[i]>>4)&0xF]; out[2*i+1]=hex[data[i]&0xF]; }
    return out;
}
static std::string hmacSha256Hex(const std::string& key, const std::string& content) {
    unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len=0;
    HMAC(EVP_sha256(), key.data(), (int)key.size(),
         reinterpret_cast<const unsigned char*>(content.data()), content.size(), md, &md_len);
    return hexEncode(md, md_len);
}
static std::string randomDigits(int n){ std::string s; s.reserve(n); for(int i=0;i<n;++i) s.push_back('0'+(rand()%10)); return s; }
static std::string buildContentString(const std::string& authType,const std::string& deviceSn,const std::string& deviceType,const std::string& random,int securemode){
    std::ostringstream oss; oss << "authType"<<authType<<"deviceSn"<<deviceSn<<"deviceType"<<deviceType<<"random"<<random<<"securemode"<<securemode; return oss.str();
}
static std::string buildClientId(const std::string& deviceType,const std::string& deviceSn,const std::string& authType,const std::string& random,int securemode){
    std::ostringstream oss; oss<<deviceType<<"."<<deviceSn<<"|securemode="<<securemode<<",authType="<<authType<<",random="<<random<<",signMethod=hmacsha256"; return oss.str();
}
static bool readTextFile(const std::string& path, std::string& out){ std::ifstream ifs(path); if(!ifs.is_open()) return false; std::ostringstream ss; ss<<ifs.rdbuf(); out=ss.str(); out.erase(std::remove(out.begin(), out.end(), '\r'), out.end()); out.erase(std::remove(out.begin(), out.end(), '\n'), out.end()); return true; }

static void usage(){
    std::cout << "用法:\n"
              << "  mqtt-pub --config <dir> data\n"
              << "  mqtt-pub --config <dir> raw --topic <topic> --payload '<json>'\n\n"
              << "示例:\n  mqtt-pub --config ./config data\n  mqtt-pub --config ./config raw --topic up/gateway/report/data --payload '{...}'\n"; }

int main(int argc, char** argv){
    std::string configDir = "./config"; std::string mode; std::string topic; std::string payloadArg; int qos=1; bool retain=false;
    for(int i=1;i<argc;++i){ std::string a=argv[i];
        if(a=="--config" && i+1<argc) configDir=argv[++i];
        else if(a=="data"||a=="raw") mode=a;
        else if(a=="--topic" && i+1<argc) topic=argv[++i];
        else if(a=="--payload" && i+1<argc) payloadArg=argv[++i];
        else if(a=="--qos" && i+1<argc) qos=std::max(0,std::min(2, atoi(argv[++i])));
        else if(a=="--retain") retain=true;
        else if(a=="-h"||a=="--help"){ usage(); return 0; }
    }
    if(mode.empty()){ usage(); return 1; }

    auto& cfg = Config::getInstance();
    if(!cfg.load(configDir)){
        std::cerr << "ERROR: 加载配置失败: " << configDir << std::endl; return 2;
    }
    const MqttConfig& m = cfg.getMqtt();

    const std::string host=m.host; const int port=m.port>0?m.port:1883; const bool useTls=m.useTls; const int keepAlive=m.keepAlive>0?m.keepAlive:60; const bool clean=m.cleanSession;
    const std::string deviceSn = !m.deviceSn.empty()? m.deviceSn : (!m.clientId.empty()? m.clientId : std::string("ems"));
    const std::string deviceType = !m.deviceType.empty()? m.deviceType : std::string("0");

    const int securemode = useTls?1:2; const std::string random = randomDigits(8);
    const std::string content = buildContentString("login", deviceSn, deviceType, random, securemode);
    std::string username = deviceSn + "&" + deviceType; std::string password; std::string clientId = buildClientId(deviceType, deviceSn, "login", random, securemode);
    std::string deviceSecret; std::string secretPath = configDir + "/device_secret_" + deviceSn + ".txt";
    if(!readTextFile(secretPath, deviceSecret) || deviceSecret.empty()){
        if(!m.password.empty() && (!m.signMethod.empty())) deviceSecret = m.password; else password = m.password;
    }
    if(password.empty()){
        if(deviceSecret.empty()) std::cerr << "WARN: 未找到 deviceSecret，将尝试使用明文密码(如已配置)" << std::endl; else password = hmacSha256Hex(deviceSecret, content);
    }

    // 构造 payload（data 模式）
    std::string payload;
    std::string dataTopic = m.topics.count("data_upload")? m.topics.at("data_upload") : std::string("up/gateway/report/data");
    if(mode=="data"){
        long long epoch = (long long)std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
        std::ostringstream ss;
        ss << "{\"cmdId\":312,\"gwId\":\"" << deviceSn << "\",\"data\":{\"time\":" << epoch
           << ",\"dSn\":\"" << deviceSn << "\"}}"; // 不包含 _test_ping
        payload = ss.str();
        topic = dataTopic;
    } else {
        if(topic.empty() || payloadArg.empty()){ usage(); return 3; }
        payload = payloadArg;
    }

    MqttClient cli; cli.setKeepAlive(keepAlive); cli.setCleanSession(clean); cli.setAutoReconnect(false);
    if(!cli.initialize(clientId, host, port)){ std::cerr<<"ERROR: 初始化失败"<<std::endl; return 4; }
    if(!cli.setCredentials(username, password)){ std::cerr<<"ERROR: 设置用户名/密码失败: "<<cli.getLastError()<<std::endl; return 5; }
    if(useTls){ if(!m.caFile.empty() || !m.certFile.empty() || !m.keyFile.empty()){ if(!cli.setTLS(m.caFile, m.certFile, m.keyFile)){ std::cerr<<"ERROR: 设置TLS失败: "<<cli.getLastError()<<std::endl; return 6; } } }

    std::atomic<bool> connected{false};
    cli.setConnectCallback([&](bool ok){ connected=ok; std::cout<<(ok?"CONNECTED":"CONNECT FAILED")<<std::endl; });
    if(!cli.connect()){ std::cerr<<"ERROR: 连接失败: "<<cli.getLastError()<<std::endl; return 7; }
    cli.startLoop(); std::this_thread::sleep_for(std::chrono::milliseconds(150));

    ems::protocol::MqttQoS q = ems::protocol::MqttQoS::QOS_1; if(qos==0) q=ems::protocol::MqttQoS::QOS_0; else if(qos==2) q=ems::protocol::MqttQoS::QOS_2;
    bool ok = cli.publish(topic, payload, q, retain);
    std::cout << (ok?"PUBLISH OK":"PUBLISH FAIL") << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(300));
    cli.stopLoop(); cli.disconnect();
    return ok?0:8;
}
